<!DOCTYPE html>
<html lang="en">

<head>
    <script src="static/js/three.min.js"></script>
    <style>
        @import 'static/css/404.css';

        html,
        body {
            font-family: 'Roboto Mono', monospace;
            background: #ba2a17;
            height: 100%;
            overflow: hidden;
        }

        .container {
            height: 100%;
            width: 100%;
            justify-content: center;
            align-items: center;
            display: flex;
        }

        .text {
            font-weight: 100;
            font-size: 28px;
            color: #FFFFFF;
            top: 55%;
            bottom: 50%;
        }

        .dud {
            color: #757575;
        }
    </style>

    <style type="text/css">
        body {
            background: #ba2a17;
            overflow: hidden;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
            -webkit-touch-callout: none;
            -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
        }

        #wrap {
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            overflow: hidden;
        }

        #wrap canvas {
            position: absolute;
            left: 50%;
            top: 50%;
            width: 500px;
            height: 500px;
            margin: -250px 0 0 -250px;
            -outline: 1px solid #fff;
        }

        .info {
            position: absolute;
            left: 0;
            right: 0;
            bottom: 0;
            font-size: 12px;
            color: #ccc;
            line-height: 2em;
            text-align: center;
        }
    </style>
</head>

<body>
    <div class="loader">
        <div class="container">
            <div class="text" style="position: absolute; z-index: 9999;font-size: 32px;text-align: center;"></div>
        </div>
        <div id="wrap"></div>
    </div>


    <script>
        class TextScramble {
            constructor(el) {
                this.el = el
                this.chars = '!<>-_\\/[]{}—=+*^?#________'
                this.update = this.update.bind(this)
            }

            setText(newText) {
                const oldText = this.el.innerText
                const length = Math.max(oldText.length, newText.length)
                const promise = new Promise((resolve) => this.resolve = resolve)
                this.queue = []
                for (let i = 0; i < length; i++) {
                    const from = oldText[i] || ''
                    const to = newText[i] || ''
                    const start = Math.floor(Math.random() * 40)
                    const end = start + Math.floor(Math.random() * 40)
                    this.queue.push({
                        from,
                        to,
                        start,
                        end
                    })
                }
                cancelAnimationFrame(this.frameRequest)
                this.frame = 0
                this.update()
                return promise
            }

            update() {
                let output = ''
                let complete = 0
                for (let i = 0, n = this.queue.length; i < n; i++) {
                    let {
                        from,
                        to,
                        start,
                        end,
                        char
                    } = this.queue[i]
                    if (this.frame >= end) {
                        complete++
                        output += to
                    } else if (this.frame >= start) {
                        if (!char || Math.random() < 0.28) {
                            char = this.randomChar()
                            this.queue[i].char = char
                        }
                        output += `<span class="dud">${char}</span>`
                    } else {
                        output += from
                    }
                }
                this.el.innerHTML = output
                if (complete === this.queue.length) {
                    this.resolve()
                } else {
                    this.frameRequest = requestAnimationFrame(this.update)
                    this.frame++
                }
            }

            randomChar() {
                return this.chars[Math.floor(Math.random() * this.chars.length)]
            }
        }

        const text = [
            "404 Not Found",
            "404 未找到页面"
        ]

        const el = document.querySelector(".text");
        const fx = new TextScramble(el);

        let counter = 0;
        const next = () => {
            fx.setText(text[counter]).then(() => {
                setTimeout(next, 800)
            });
            counter = (counter + 1) % text.length;
        }

        next();
    </script>

    <script type="text/javascript">
        var $body = document.body,
            $wrap = document.getElementById('wrap'),

            areawidth = window.innerWidth,
            areaheight = window.innerHeight,

            canvassize = 500,

            length = 30,
            radius = 5.6,

            rotatevalue = 0.035,
            acceleration = 0,
            animatestep = 0,
            toend = false,

            pi2 = Math.PI * 2,

            group = new THREE.Group(),
            mesh, ringcover, ring,

            camera, scene, renderer;


        camera = new THREE.PerspectiveCamera(65, 1, 1, 10000);
        camera.position.z = 150;

        scene = new THREE.Scene();
        // scene.add(new THREE.AxisHelper(30));
        scene.add(group);

        mesh = new THREE.Mesh(
            new THREE.TubeGeometry(new (THREE.Curve.create(function () {
            },
                function (percent) {

                    var x = length * Math.sin(pi2 * percent),
                        y = radius * Math.cos(pi2 * 3 * percent),
                        z, t;

                    t = percent % 0.25 / 0.25;
                    t = percent % 0.25 - (2 * (1 - t) * t * -0.0185 + t * t * 0.25);
                    if (Math.floor(percent / 0.25) == 0 || Math.floor(percent / 0.25) == 2) {
                        t *= -1;
                    }
                    z = radius * Math.sin(pi2 * 2 * (percent - t));

                    return new THREE.Vector3(x, y, z);

                }))(),
                200, 1.1, 2, true),
            new THREE.MeshBasicMaterial({
                color: 0xffffff
                // , wireframe: true
            }));

        group.add(mesh);

        ringcover = new THREE.Mesh(new THREE.PlaneGeometry(50, 15, 1), new THREE.MeshBasicMaterial({
            color: 0x000000,
            opacity: 0,
            transparent: true
        }));
        ringcover.position.x = length + 1;
        ringcover.rotation.y = Math.PI / 2;
        group.add(ringcover);

        ring = new THREE.Mesh(new THREE.RingGeometry(4.3, 5.55, 32), new THREE.MeshBasicMaterial({
            color: 0xffffff,
            opacity: 0,
            transparent: true
        }));
        ring.position.x = length + 1.1;
        ring.rotation.y = Math.PI / 2;
        group.add(ring);

        // fake shadow
        (function () {
            var plain, i;
            for (i = 0; i < 10; i++) {
                plain = new THREE.Mesh(new THREE.PlaneGeometry(length * 2 + 1, radius * 3, 1), new THREE.MeshBasicMaterial({
                    color: 0xba2a17,
                    transparent: true,
                    opacity: 0.13
                }));
                plain.position.z = -2.5 + i * 0.5;
                group.add(plain);
            }
        })();

        renderer = new THREE.WebGLRenderer({
            antialias: true
        });

        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(canvassize, canvassize);
        renderer.setClearColor('#ba2a17');

        $wrap.appendChild(renderer.domElement);

        animate();


        function tilt(percent) {
            group.rotation.y = percent * 0.5;
        }

        function render() {

            var progress;

            animatestep = Math.max(0, Math.min(240, toend ? animatestep + 1 : animatestep - 4));
            acceleration = easing(animatestep, 0, 1, 240);

            if (acceleration > 0.35) {
                progress = (acceleration - 0.35) / 0.65;
                group.rotation.y = -Math.PI / 2 * progress;
                group.position.z = 50 * progress;
                progress = Math.max(0, (acceleration - 0.97) / 0.03);
                mesh.material.opacity = 1 - progress;
                ringcover.material.opacity = ring.material.opacity = progress;
                ring.scale.x = ring.scale.y = 0.9 + 0.1 * progress;
            }

            renderer.render(scene, camera);

        }

        function animate() {
            mesh.rotation.x += rotatevalue + acceleration;
            render();
            requestAnimationFrame(animate);
        }

        function easing(t, b, c, d) {
            if ((t /= d / 2) < 1) return c / 2 * t * t + b;
            return c / 2 * ((t -= 2) * t * t + 2) + b;
        }

        //# sourceURL=pen.js
    </script>
</body>

</html>